package devicemanager

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"dev.ixmxm.top/rms/boot"
	"dev.ixmxm.top/rms/codes"
	"dev.ixmxm.top/rms/config"
	"dev.ixmxm.top/rms/managers/robotmanager"
	"dev.ixmxm.top/rms/managers/taskmanager"
	"dev.ixmxm.top/rms/model"
	"dev.ixmxm.top/rms/utils/function"
	"dev.ixmxm.top/rms/utils/json"
	"dev.ixmxm.top/rms/utils/log"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type DeviceType uint8

const (
	TypeAutoDoor       DeviceType = 9  //自动门
	TypeElevator       DeviceType = 10 //电梯
	TypeConveyorLine   DeviceType = 11 //输送线
	TypeManipulator    DeviceType = 12 //机械手
	TypeHoist          DeviceType = 13 //提升机
	TypePlatformWeight DeviceType = 14 //称重平台
	TypeGroundWeight   DeviceType = 15 //地称
)

type deviceStatus uint8

const (
	//0=未知、1=已开启、2=已关闭、3=运行中、4=已停止、5=可放货、6=可取货、7=故障、8=空闲、9=完成、10=初始化
	devStatusUnknown deviceStatus = 0
	devStatusOpen    deviceStatus = 1
	devStatusClose   deviceStatus = 2
	devStatusRunning deviceStatus = 3
	devStatusStopped deviceStatus = 4
	devStatusCanPut  deviceStatus = 5
	devStatusCanPick deviceStatus = 6
	devStatusError   deviceStatus = 7
	devStatusIdle    deviceStatus = 8
	devStatusDone    deviceStatus = 9
	devStatusInit    deviceStatus = 10
)

func New(db *boot.MongoDB, tm *taskmanager.TaskManager) *DeviceManager {
	m := &DeviceManager{
		taskManager: tm,
		db:          db,
	}
	m.httpClient = http.DefaultClient
	m.devices = make(map[string]IDevice, 0)
	m.elevatorMap = make(map[string]*model.Elevator, 0)
	m.doorMap = make(map[string]*model.Door, 0)
	m.elevatorInfos = make(map[string]model.ElevatorStatus, 0)
	m.doorInfos = make(map[string]model.DoorInfo, 0)
	//初始换电梯缓存数据
	_ = m.loadAllElevatorToCache(context.Background())
	return m
}

type DeviceManager struct {
	httpClient    *http.Client
	devices       map[string]IDevice
	elevatorMap   map[string]*model.Elevator //内存中的电梯数据
	doorMap       map[string]*model.Door     //内存中的门的数据
	doorInfos     map[string]model.DoorInfo
	elevatorInfos map[string]model.ElevatorStatus
	statusMutex   sync.RWMutex
	doorMutex     sync.RWMutex             //电梯互斥锁
	elevatorMutex sync.RWMutex             //电梯互斥锁
	db            *boot.MongoDB            //数据库对象
	taskManager   *taskmanager.TaskManager //任务模块对象
	SystemConfig  *model.SystemConfig      //系统配置(缓存),不可直接赋值保存，调用SaveSystemConfigToCache保存
}

//按照电梯编号获得电梯数据
func (m *DeviceManager) GetFromCache(deviceCode string) *model.Elevator {
	m.elevatorMutex.Lock()
	defer m.elevatorMutex.Unlock()
	return m.elevatorMap[deviceCode]
}

//获得所有的电梯数据
func (m *DeviceManager) GetAllFromCache() map[string]*model.Elevator {
	m.elevatorMutex.Lock()
	defer m.elevatorMutex.Unlock()
	return m.elevatorMap
}

func (devManger *DeviceManager) Load(mapName string, nodes map[string]model.Node) codes.Code {
	for id, device := range devManger.devices {
		if device.GetMapName() == mapName {
			delete(devManger.devices, id)
		}
	}
	for _, node := range nodes {
		if node.DeviceID == "" {
			continue
		}
		if tmp, find := devManger.devices[node.DeviceID]; find && tmp.GetMapName() == mapName {
			return codes.ErrMapSameDevice
		}
		var dev IDevice
		base := deviceBase{id: node.DeviceID, mapName: mapName, siteID: node.Id}
		switch node.Type {
		case model.NodeAutoDoor:
			base.devType = TypeAutoDoor
			dev = NewAutoDoor(base)

			devManger.doorMap[node.DeviceID] = &model.Door{DeviceCode: node.DeviceID, Status: 0, MapName: mapName, IsOnline: true, IsEnable: true, AgvId: ""}
		case model.NodeElevator:
			base.devType = TypeElevator
			dev = NewElevator(base)
		case model.NodeConveyorLine:
			base.devType = TypeConveyorLine
			dev = NewConveyorLine(base)
		case model.NodeManipulator:
			base.devType = TypeManipulator
		case model.NodeHoist:
			base.devType = TypeHoist
		case model.NodePlatformWeight:
			base.devType = TypePlatformWeight
			dev = NewConveyorLine(base)
		case model.NodeGroundWeight:
			base.devType = TypeGroundWeight
		}
		if dev == nil {
			continue
		}
		log.Infof("添加设备:地图:%s,类型:%d,设备编号:%s", mapName, node.Type, node.DeviceID)
		devManger.devices[node.DeviceID] = dev
	}
	return nil
}

func (devManger *DeviceManager) OperatorAll(ctx context.Context, node model.Node, agvId, map_name string, operatorCode model.OperatorCode) codes.Code {

	mianTask, err1 := devManger.taskManager.GetMainTaskByAgvFromDB(ctx, agvId)
	if err1 != nil {
		return err1
	}
	if mianTask == nil {
		return nil
	}
	var taskId uint64 = 0
	if mianTask != nil {
		if mianTask.TaskId == 0 {
			return nil
		}
		taskId = mianTask.TaskId
	}

	switch node.Type {
	case model.NodeAutoDoor:
		if operatorCode == model.OperatorOpen {
			//执行开门操作
			return devManger.Open(node.DeviceID, agvId, map_name, taskId)
		} else if operatorCode == model.OperatorClose {
			//执行关门操作
			return devManger.Close(node.DeviceID, agvId, map_name, taskId)
		}
	case model.NodeConveyorLine:
		task, _ := devManger.taskManager.GetTaskFromDB(ctx, taskId)
		if task == nil {
			return codes.ErrCallService.WithDetails("未找到对应任务")
		}
		switch task.Type {
		case model.TaskTypePick:
			//执行取货操作
			return devManger.Pick(node.DeviceID, agvId, map_name, taskId, task)
		case model.TaskTypePut:
			//执行放货操作
			return devManger.Put(node.DeviceID, agvId, map_name, taskId, task)
		default:
			return codes.ErrCallService.WithDetails("该任务类型不可到达输送线")
		}
	case model.NodeElevator:
		if operatorCode == model.OperatorOpen {
			//进门
			return devManger.Open(node.DeviceID, agvId, map_name, taskId)
		} else if operatorCode == model.OperatorSwitch {
			//切层
			return devManger.SwitchFloor(node.DeviceID, agvId, map_name, taskId)
		} else if operatorCode == model.OperatorOutOpen {
			//出门
			return devManger.OutOpen(node.DeviceID, agvId, map_name, taskId)
		} else if operatorCode == model.OperatorClose {
			//关门
			return devManger.Close(node.DeviceID, agvId, map_name, taskId)
		}
	case model.NodePlatformWeight:
		task, _ := devManger.taskManager.GetTaskFromDB(ctx, taskId)
		if task == nil {
			return codes.ErrCallService.WithDetails("未找到对应任务")
		}
		//查询当前主要任务
		subTaskArr, err2 := devManger.taskManager.GetAllSubtasksByTaskIdFromDB(ctx, taskId)
		if err2 != nil || len(subTaskArr) <= 0 {
			return codes.ErrCallService.WithDetails("未找到对应的子任务")
		}
		//按照顺判断当前执行到哪个步骤
		var findSubTaskType model.TaskType
		findSubTask := false
		for _, valSub := range subTaskArr {
			if valSub.Status != model.TaskStatusCanceled && valSub.Status != model.TaskStatusFinish {
				findSubTask = true
				findSubTaskType = valSub.Type
				break
			}
		}
		if !findSubTask {
			return nil
		}
		switch findSubTaskType {
		case model.TaskTypePick:
			//执行取货操作
			return devManger.WeightingPick(node.DeviceID, agvId, map_name, taskId, task)
		case model.TaskTypePut:
			//执行放货操作
			return devManger.WeightingPut(node.DeviceID, agvId, map_name, taskId, task)
		default:
			return codes.ErrCallService.WithDetails("该任务类型不可操作称重平台!!!")
		}
		// if operatorCode == model.OperatorPick {
		// 	return devManger.WeightingPick(node.DeviceID, agvId, map_name, taskId)
		// } else if operatorCode == model.OperatorWeighPut {
		// 	return devManger.WeightingPut(node.DeviceID, agvId, map_name, taskId)
		// }
	}
	return codes.ErrDeviceNotSupport.WithDetails("暂不支持此设备")
}

func (devManger *DeviceManager) Open(deviceID string, agvID string, toMap string, taskId uint64) codes.Code {
	log.Warnf("设备%s进开门 所在地图%s 任务编号：%d", deviceID, toMap, taskId)
	return devManger.call(model.OperatorOpen, deviceID, agvID, toMap, taskId, nil)
}
func (devManger *DeviceManager) OutOpen(deviceID string, agvID string, toMap string, taskId uint64) codes.Code {
	log.Warnf("设备%s出开门 所在地图%s 任务编号：%d", deviceID, toMap, taskId)
	return devManger.call(model.OperatorOutOpen, deviceID, agvID, toMap, taskId, nil)
}

func (devManger *DeviceManager) Pick(deviceID string, agvID string, toMap string, taskId uint64, task *model.Task) codes.Code {
	log.Warnf("输送线：%s 请求取货 所在地图%s 任务编号：%d", deviceID, toMap, taskId)
	return devManger.call(model.OperatorPick, deviceID, agvID, toMap, taskId, task)
}
func (devManger *DeviceManager) Put(deviceID string, agvID string, toMap string, taskId uint64, task *model.Task) codes.Code {
	log.Warnf("输送线：%s 请求放货 所在地图%s 任务编号：%d", deviceID, toMap, taskId)
	return devManger.call(model.OperatorPut, deviceID, agvID, toMap, taskId, task)
}

func (devManger *DeviceManager) Close(deviceID string, agvID string, toMap string, taskId uint64) codes.Code {
	log.Warnf("robot%s 设备%s关门 所在地图%s 任务编号：%d", agvID, deviceID, toMap, taskId)
	return devManger.call(model.OperatorClose, deviceID, agvID, toMap, taskId, nil)
}
func (devManger *DeviceManager) SwitchFloor(deviceID string, agvID, toMap string, taskId uint64) codes.Code {
	log.Warnf("robot%s 设备%s切换楼层 目标楼层%s 任务编号：%d", agvID, deviceID, toMap, taskId)
	return devManger.call(model.OperatorSwitch, deviceID, agvID, toMap, taskId, nil)
}
func (devManger *DeviceManager) WeightingPick(deviceID string, agvID, toMap string, taskId uint64, task *model.Task) codes.Code {
	log.Warnf("robot%s 设备%s 即将取货称重 目标地图%s 任务编号：%d", agvID, deviceID, toMap, taskId)
	return devManger.call(model.OperatorWeighPick, deviceID, agvID, toMap, taskId, task)
}
func (devManger *DeviceManager) WeightingPut(deviceID string, agvID, toMap string, taskId uint64, task *model.Task) codes.Code {
	log.Warnf("robot%s 设备%s 即将在平台放货(称重验证) 目标地图%s 任务编号：%d", agvID, deviceID, toMap, taskId)
	return devManger.call(model.OperatorWeighPut, deviceID, agvID, toMap, taskId, task)
}

func (devManger *DeviceManager) OperatorApplyArea(ctx context.Context, area model.Area, agvId, map_name string, operatorCode model.OperatorCode) codes.Code {

	mianTask, err1 := devManger.taskManager.GetMainTaskByAgvFromDB(ctx, agvId)
	if err1 != nil {
		return err1
	}
	if mianTask == nil {
		return nil
	}
	var taskId uint64 = 0
	if mianTask != nil {
		if mianTask.TaskId == 0 {
			return nil
		}
		taskId = mianTask.TaskId
	}
	if operatorCode == model.OperatorOpen {
		//执行开门操作
		return devManger.GoInArea(area.Id, agvId, map_name, taskId)
	} else if operatorCode == model.OperatorClose {
		//执行关门操作
		return devManger.GoOutArea(area.Id, agvId, map_name, taskId)
	}

	return codes.ErrDeviceNotSupport.WithDetails("暂不支持此区域")
}
func (devManger *DeviceManager) GoInArea(areaId string, agvID string, toMap string, taskId uint64) codes.Code {
	log.Warnf("申请进入区域:%s 所在地图%s 任务编号：%d", areaId, toMap, taskId)
	return devManger.applyGoDoorArea(model.OperatorOpen, areaId, agvID, toMap, taskId, nil)
}
func (devManger *DeviceManager) GoOutArea(areaId string, agvID string, toMap string, taskId uint64) codes.Code {
	log.Warnf("申请离开区域:%s 所在地图%s 任务编号：%d", areaId, toMap, taskId)
	return devManger.applyGoDoorArea(model.OperatorOutOpen, areaId, agvID, toMap, taskId, nil)
}

func (devManger *DeviceManager) applyGoDoorArea(code model.OperatorCode, areaId string, agvID, toMap string, taskId uint64, task *model.Task) codes.Code {
	currentRobot := robotmanager.GetFromCache(agvID)
	var (
		needStatus deviceStatus
	)
	agvCurrentSiteId := ""
	if currentRobot != nil && currentRobot.StatusIsExists() {
		agvCurrentSiteId = currentRobot.Status.SiteIdS
	}
	cmd := make(map[string]interface{})
	cmd["device_code"] = areaId
	cmd["map_name"] = toMap
	cmd["site_id"] = areaId
	cmd["agv_site_id"] = agvCurrentSiteId
	//cmd["device_type"] = dev.GetDeviceType()
	cmd["agv_id"] = agvID
	cmd["task_id"] = taskId
	subcmd := make(map[string]interface{})

	doorCmd := model.NormalDoor

	switch code {
	case model.OperatorOpen:
		subcmd["open_close"] = 1
		subcmd["map_name"] = toMap
		subcmd["door"] = doorCmd
		needStatus = devStatusOpen
	case model.OperatorClose:
		subcmd["open_close"] = 2
		subcmd["map_name"] = toMap
		subcmd["door"] = doorCmd
		needStatus = devStatusClose
	}
	cmd["operate_cmd"] = subcmd
	httpClient := http.Client{Timeout: 5 * time.Second}
	data := strings.NewReader(json.DumpString(cmd))
	//请求的数据
	log.Warnf("申请进入门区域数据：%+v", data)
	url := fmt.Sprintf("http://%s:%d/efork_api/device_door_area", devManger.SystemConfig.MiddlewareCfg.Ip, devManger.SystemConfig.MiddlewareCfg.PortApi)
	resp, errDo := httpClient.Post(url, "application/json", data)
	//req, err := http.NewRequest(http.MethodPost, devManger.url, bytes.NewBuffer(json.Dump(cmd)))
	//req.Header.Set()
	// if err != nil {
	// 	log.Errorf("make http request error:%s", err.Error())
	// 	return codes.ErrMakeHttpRequest.Wrap(err)
	// }
	//resp, errDo := devManger.httpClient.Do(req)
	if errDo != nil {
		log.Errorf("send http request error:%s", errDo.Error())
		return codes.ErrDoHttpRequest.Wrap(errDo)
	}
	res, errRead := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if errRead != nil {
		log.Errorf("read http response body data error:%s", errRead.Error())
		return codes.ErrDoHttpRequest.Wrap(errRead)
	}
	reply := &struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			Online       bool         `json:"online"`
			Enable       bool         `json:"enable"`
			DeviceStatus deviceStatus `json:"device_status"`
		}
	}{}
	res = bytes.TrimPrefix(res, []byte("\xef\xbb\xbf"))
	if errBind := json.Bind(res, reply); errBind != nil {
		log.Errorf("bind http response body data error:%s", errBind.Error())
		return codes.ErrDoHttpRequest.Wrap(errBind)
	}
	if reply.Code != 0 {
		log.Errorf("call device operate failed,code:%d,message:%s", reply.Code, reply.Message)
		return codes.ErrDeviceNotEnable
	}
	if !reply.Data.Enable {
		return codes.ErrDeviceNotEnable
	}
	if !reply.Data.Online {
		return codes.ErrDeviceNotOnline
	}
	if needStatus != reply.Data.DeviceStatus {
		// log.Infof("device status not match,need:%d,current:%d", needStatus, reply.Data.DeviceStatus)
		return codes.ErrDeviceStatusNotMatch
	}
	return nil
}

//申请设备
func (devManger *DeviceManager) call(code model.OperatorCode, deviceID string, agvID, toMap string, taskId uint64, task *model.Task) codes.Code {
	dev, find := devManger.devices[deviceID]
	if !find {
		log.Errorf("device :%d not existed", deviceID)
		return codes.ErrDeviceNotExists
	}
	var (
		needStatus deviceStatus
	)
	currentRobot := robotmanager.GetFromCache(agvID)
	agvCurrentSiteId := "-1"
	if currentRobot != nil && currentRobot.StatusIsExists() {
		agvCurrentSiteId = currentRobot.Status.SiteIdS
	}

	//特殊情况不允许关门
	if dev.GetDeviceType() == TypeAutoDoor && code == model.OperatorClose {
		//判断当前点在下面不能关门
		if agvCurrentSiteId == "-1" {
			log.Error("当前小车定位丢失或者无状态不允许关门！！！")
			//不确定车的位置不能关门
			return codes.ErrNotCloseDoor
		}
		if agvCurrentSiteId == dev.GetSiteID() {
			log.Error("当前小车在自动们点不允许关门！！！")
			return codes.ErrNotCloseDoor
		}
	}
	mianTaskCode := "0"
	if task != nil {
		mianTaskCode = task.MainTaskCode
	}
	cmd := make(map[string]interface{})
	cmd["device_code"] = dev.GetID()
	cmd["map_name"] = dev.GetMapName()
	cmd["site_id"] = dev.GetSiteID()
	cmd["agv_site_id"] = agvCurrentSiteId
	//cmd["device_type"] = dev.GetDeviceType()
	cmd["agv_id"] = agvID
	cmd["task_id"] = taskId
	cmd["main_task_code"] = mianTaskCode
	subcmd := make(map[string]interface{})

	doorCmd := model.NormalDoor
	if ele, find := devManger.elevatorMap[deviceID]; find {
		for _, info := range ele.InfoList {
			if info.MapName == toMap {
				doorCmd = info.Door
			}
		}
	}
	switch code {
	case model.OperatorOpen:
		subcmd = dev.OpenCMD()
		subcmd["map_name"] = toMap
		subcmd["door"] = doorCmd
		needStatus = dev.OpenStatus()
	case model.OperatorClose:
		subcmd = dev.CloseCMD()
		subcmd["map_name"] = toMap
		subcmd["door"] = doorCmd
		needStatus = dev.CloseStatus()
	case model.OperatorOutOpen:
		subcmd["elevator_cmd"] = 2
		subcmd["map_name"] = toMap
		subcmd["door"] = doorCmd
		needStatus = dev.OpenStatus()
	case model.OperatorSwitch:
		subcmd["elevator_cmd"] = 3
		subcmd["map_name"] = toMap
		needStatus = devStatusDone
	case model.OperatorPick:
		subcmd["pick_put"] = 1
		subcmd["to_location"] = task.TargetSiteID
		needStatus = devStatusCanPick
	case model.OperatorPut:
		subcmd["pick_put"] = 2
		subcmd["to_location"] = task.TargetSiteID
		needStatus = devStatusCanPut
	case model.OperatorWeighPick:
		subcmd["weighing_platform_cmd"] = 1
		needStatus = devStatusCanPick
	case model.OperatorWeighPut:
		subcmd["weighing_platform_cmd"] = 0
		needStatus = devStatusCanPut
	}
	cmd["operate_cmd"] = subcmd
	timeOut := config.Conf.CallTimeout
	httpClient := http.Client{Timeout: time.Duration(timeOut) * time.Second}
	data := strings.NewReader(json.DumpString(cmd))
	url := ""
	switch dev.GetDeviceType() {
	case TypeAutoDoor:
		url = fmt.Sprintf("http://%s:%d/efork_api/device_auto_door_operate", devManger.SystemConfig.MiddlewareCfg.Ip, devManger.SystemConfig.MiddlewareCfg.PortApi)
		break
	case TypeConveyorLine:
		url = fmt.Sprintf("http://%s:%d/efork_api/device_conveyor_line_operate", devManger.SystemConfig.MiddlewareCfg.Ip, devManger.SystemConfig.MiddlewareCfg.PortApi)
		break
	case TypeElevator:
		url = fmt.Sprintf("http://%s:%d/efork_api/device_elevator_operate", devManger.SystemConfig.MiddlewareCfg.Ip, devManger.SystemConfig.MiddlewareCfg.PortApi)
		break
	case TypePlatformWeight:
		url = fmt.Sprintf("http://%s:%d/efork_api/device_weighing_platform_operate", devManger.SystemConfig.MiddlewareCfg.Ip, devManger.SystemConfig.MiddlewareCfg.PortApi)
		break
	default:
		return codes.ErrDeviceStatusNotMatch
	}
	resp, errDo := httpClient.Post(url, "application/json", data)
	//req, err := http.NewRequest(http.MethodPost, devManger.url, bytes.NewBuffer(json.Dump(cmd)))
	//req.Header.Set()
	// if err != nil {
	// 	log.Errorf("make http request error:%s", err.Error())
	// 	return codes.ErrMakeHttpRequest.Wrap(err)
	// }
	//resp, errDo := devManger.httpClient.Do(req)
	if errDo != nil {
		log.Errorf("send http request error:%s", errDo.Error())
		return codes.ErrDoHttpRequest.Wrap(errDo)
	}
	res, errRead := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if errRead != nil {
		log.Errorf("read http response body data error:%s", errRead.Error())
		return codes.ErrDoHttpRequest.Wrap(errRead)
	}
	reply := &struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			Online       bool         `json:"online"`
			Enable       bool         `json:"enable"`
			DeviceStatus deviceStatus `json:"device_status"`
		}
	}{}
	res = bytes.TrimPrefix(res, []byte("\xef\xbb\xbf"))
	if errBind := json.Bind(res, reply); errBind != nil {
		log.Errorf("bind http response body data error:%s", errBind.Error())
		return codes.ErrDoHttpRequest.Wrap(errBind)
	}
	if reply.Code != 0 {
		log.Errorf("call device operate failed,code:%d,message:%s", reply.Code, reply.Message)
		return codes.ErrDeviceNotEnable
	}
	if !reply.Data.Enable {
		return codes.ErrDeviceNotEnable
	}
	if !reply.Data.Online {
		return codes.ErrDeviceNotOnline
	}
	if needStatus != reply.Data.DeviceStatus {
		// log.Infof("device status not match,need:%d,current:%d", needStatus, reply.Data.DeviceStatus)
		return codes.ErrDeviceStatusNotMatch
	}
	return nil
}

//获取货架状态
func (devManger *DeviceManager) GetGoodsShelvesStauts(mapName string, siteIds []string) (map[string]struct {
	SiteId      string `json:"site_id"`
	Layer       int    `json:"layer"`
	GoodsStatus int    `json:"goods_status"`
}, bool, codes.Code) {
	if len(siteIds) < 1 {
		return nil, false, codes.ErrInvalidParams
	}
	httpClient := http.Client{Timeout: 5 * time.Second}
	url := fmt.Sprintf("http://%s:%d/efork_api/get_goods_shelves_stauts?map_name=%s", devManger.SystemConfig.MiddlewareCfg.Ip, devManger.SystemConfig.MiddlewareCfg.PortApi, mapName)
	for _, siteId := range siteIds {
		url += "&site_ids=" + siteId
	}
	resp, errDo := httpClient.Get(url)
	if errDo != nil {
		log.Errorf("send http request error:%s", errDo.Error())
		return nil, false, codes.ErrDoHttpRequest.Wrap(errDo)
	}
	res, errRead := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if errRead != nil {
		log.Errorf("read http response body data error:%s", errRead.Error())
		return nil, false, codes.ErrDoHttpRequest.Wrap(errRead)
	}
	reply := &struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    []struct {
			SiteId      string `json:"site_id"`
			Layer       int    `json:"layer"`
			GoodsStatus int    `json:"goods_status"`
		}
	}{}
	res = bytes.TrimPrefix(res, []byte("\xef\xbb\xbf"))
	if errBind := json.Bind(res, reply); errBind != nil {
		log.Errorf("bind http response body data error:%s", errBind.Error())
		return nil, false, codes.ErrDoHttpRequest.Wrap(errBind)
	}
	if reply.Code != 0 {
		log.Errorf("call device operate failed,code:%d,message:%s", reply.Code, reply.Message)
		return nil, false, codes.ErrDeviceNotEnable
	}
	goodsStatus := make(map[string]struct {
		SiteId      string `json:"site_id"`
		Layer       int    `json:"layer"`
		GoodsStatus int    `json:"goods_status"`
	}, 0)
	haveEmptyStation := false
	if len(reply.Data) > 0 {
		for _, g := range reply.Data {
			goodsStatus[g.SiteId] = g
		}

		for _, g := range reply.Data {
			cur := goodsStatus[g.SiteId]
			if cur.GoodsStatus == 0 {
				if g.GoodsStatus == 0 && g.Layer < cur.Layer {
					goodsStatus[g.SiteId] = g
				}
				haveEmptyStation = true
			} else {
				if g.GoodsStatus == 0 {
					goodsStatus[g.SiteId] = g
					haveEmptyStation = true
				} else {
					if g.Layer < cur.Layer {
						goodsStatus[g.SiteId] = g
					}
				}
			}
		}
	}
	return goodsStatus, haveEmptyStation, nil
}

//***************数据库相关
//1.电梯设置
//新增和修改电梯
func (m *DeviceManager) AddElevatorToDB(ctx context.Context, deviceCode string, elevator *model.Elevator) codes.Code {
	elevator.Online = true
	dbRes := m.db.Collection(ctx, model.Elevator{}.TableName()).FindOneAndReplace(ctx, bson.M{"device_code": deviceCode}, elevator, &options.FindOneAndReplaceOptions{Upsert: function.BoolPtr(true)})
	if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "insert or update elevator to db error:%s", dbRes.Err())
		return codes.ErrDatabase.Wrap(dbRes.Err())
	}

	//更新缓存
	m.elevatorMutex.Lock()
	defer m.elevatorMutex.Unlock()
	m.elevatorMap[deviceCode] = elevator
	return nil
}

//删除电梯
func (m *DeviceManager) DeleteElevatorByDeviceCode(ctx context.Context, deviceCode string) codes.Code {
	_, err := m.db.Collection(ctx, model.Elevator{}.TableName()).DeleteOne(ctx, bson.M{"device_code": deviceCode})
	if err != nil && !errors.Is(err, mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "delete elevator from db error:%s", err.Error())
		return codes.ErrDatabase.Wrap(err)
	}
	m.elevatorMutex.Lock()
	defer m.elevatorMutex.Unlock()
	delete(m.elevatorMap, deviceCode)
	return nil
}

//获取电梯信息
func (m *DeviceManager) GetAllElevatorInfoFromDB(ctx context.Context) ([]model.Elevator, codes.Code) {
	elevatorArr := make([]model.Elevator, 0)
	res, err := m.db.Collection(ctx, model.Elevator{}.TableName()).Find(ctx, bson.M{})
	if err != nil {
		log.ErrorfWithContext(ctx, "get all elevator from db error:%s", err.Error())
		return nil, codes.ErrDatabase.Wrap(err)
	}
	if errBind := res.All(ctx, &elevatorArr); errBind != nil {
		log.ErrorfWithContext(ctx, "bind all elevator error:%s", errBind.Error())
		return nil, codes.ErrDatabase.Wrap(errBind)
	}
	return elevatorArr, nil
}

//获取电梯信息
func (m *DeviceManager) GetElevatorFromDB(ctx context.Context, deviceCode string) (*model.Elevator, codes.Code) {
	dbRes := m.db.Collection(ctx, model.Elevator{}.TableName()).FindOne(ctx, bson.M{"device_code": deviceCode})
	if dbRes.Err() != nil && !errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		log.ErrorfWithContext(ctx, "get robot main task from db error:%s", dbRes.Err().Error())
		return nil, codes.ErrDatabase.Wrap(dbRes.Err())
	}
	if dbRes.Err() != nil && errors.Is(dbRes.Err(), mongo.ErrNoDocuments) {
		//log.Errorf("can not find robot:%s main task",agvID)
		return nil, codes.ErrTaskNotExists
	}
	elevator := new(model.Elevator)
	if errBind := dbRes.Decode(elevator); errBind != nil {
		log.ErrorfWithContext(ctx, "bind main task error:%s", errBind.Error())
		return nil, codes.ErrTaskNotExists.Wrap(errBind)
	}
	return elevator, nil
}

//更新区域切换点的占用AGV
func (m *DeviceManager) ModifyAreaSwitchOccupyToDB(ctx context.Context, mapName, siteId, agvId string) codes.Code {
	if agvId == "" {
		log.Warnf("清空区域切换%s-%s占用信息", mapName, siteId)
	} else {
		dbRes := m.db.Collection(ctx, model.MapRelation{}.TableName()).FindOne(ctx, bson.M{"from_map": mapName, "start_id": siteId})
		if dbRes.Err() != nil {
			dbRes = m.db.Collection(ctx, model.MapRelation{}.TableName()).FindOne(ctx, bson.M{"to_map": mapName, "goal_id": siteId})
			if dbRes.Err() != nil {
				// log.ErrorfWithContext(ctx, "update map-relation to db error:%s", dbRes.Err())
				return codes.ErrDatabase.Wrap(dbRes.Err())
			}
		}
		res := new(model.MapRelation)
		if errBind := dbRes.Decode(res); errBind != nil {
			log.ErrorfWithContext(ctx, "bind robot local path error:%s", errBind.Error())
			return codes.ErrDatabase.Wrap(errBind)
		}
		if res.AgvId != "" && res.AgvId != agvId {
			return codes.ErrSwitchOccupied
		}

	}
	dbRes := m.db.Collection(ctx, model.MapRelation{}.TableName()).FindOneAndUpdate(ctx, bson.M{"from_map": mapName, "start_id": siteId}, bson.M{"$set": bson.M{"agv_id": agvId}})
	if dbRes.Err() != nil {
		dbRes = m.db.Collection(ctx, model.MapRelation{}.TableName()).FindOneAndUpdate(ctx, bson.M{"to_map": mapName, "goal_id": siteId}, bson.M{"$set": bson.M{"agv_id": agvId}})
		if dbRes.Err() != nil {
			return codes.ErrDatabase.Wrap(dbRes.Err())
		} else if agvId != "" {
			m.db.Collection(ctx, model.MapRelation{}.TableName()).FindOneAndUpdate(ctx, bson.M{"to_map": mapName, "goal_id": siteId}, bson.M{"$set": bson.M{"positive_way": false}})
		}
	} else if agvId != "" {
		m.db.Collection(ctx, model.MapRelation{}.TableName()).FindOneAndUpdate(ctx, bson.M{"from_map": mapName, "start_id": siteId}, bson.M{"$set": bson.M{"positive_way": true}})
	}
	//
	log.Warnf("添加区域切换%s-%s占用信息 robot = %s", mapName, siteId, agvId)
	return nil
}

//电梯的释放(agv占用)
func (m *DeviceManager) ModifyElevatorOccupyToDB(ctx context.Context, deviceCode, agvId string) codes.Code {
	if agvId == "" {
		log.Warnf("清空电梯设备%s占用信息", deviceCode)
	} else {
		log.Warnf("添加电梯设备%s占用信息 robot = %s", deviceCode, agvId)
	}
	dbRes := m.db.Collection(ctx, model.Elevator{}.TableName()).FindOneAndUpdate(ctx, bson.M{"device_code": deviceCode}, bson.M{"$set": bson.M{"agv_id": agvId}})
	if dbRes.Err() != nil {
		log.ErrorfWithContext(ctx, "update elevator to db error:%s", dbRes.Err())
		return codes.ErrDatabase.Wrap(dbRes.Err())
	}
	//更新缓存
	m.elevatorMutex.Lock()
	defer m.elevatorMutex.Unlock()
	m.elevatorMap[deviceCode].AgvId = agvId
	return nil
}

//电梯的启用和禁用
func (m *DeviceManager) EnableElevatorToDB(ctx context.Context, deviceCode string, isEnable bool) codes.Code {
	if m.elevatorMap[deviceCode].AgvId != "" {
		return codes.ErrElevatorOccupied
	}
	dbRes := m.db.Collection(ctx, model.Elevator{}.TableName()).FindOneAndUpdate(ctx, bson.M{"device_code": deviceCode}, bson.M{"$set": bson.M{"is_enable": isEnable}})
	if dbRes.Err() != nil {
		log.ErrorfWithContext(ctx, "set elevator enable to db error:%s", dbRes.Err())
		return codes.ErrDatabase.Wrap(dbRes.Err())
	}
	//更新缓存
	m.elevatorMutex.Lock()
	defer m.elevatorMutex.Unlock()
	m.elevatorMap[deviceCode].IsEnable = isEnable
	return nil
}

//移动门的启用和禁用EnableDoor
func (m *DeviceManager) EnableDoor(ctx context.Context, deviceCode string, isEnable bool, mapName string) codes.Code {
	//更新缓存
	m.doorMutex.Lock()
	defer m.doorMutex.Unlock()
	// if m.doorMap[deviceCode].MapName!=mapName {
	// 	return codes.ErrWebsockets
	// }
	if _, ok := m.doorMap[deviceCode]; ok {
		//存在
		m.doorMap[deviceCode].IsEnable = isEnable
	} else {
		return codes.ErrDoorNotExists
	}
	return nil
}

//---------------------获得所有设备状态---------------------
func (m *DeviceManager) GetAllDeviceStatus() struct {
	DoorInfos     []model.DoorInfo       `json:"door_status"`
	ElevatorInfos []model.ElevatorStatus `json:"elevator_status"`
} {
	reply := struct {
		DoorInfos     []model.DoorInfo       `json:"door_status"`
		ElevatorInfos []model.ElevatorStatus `json:"elevator_status"`
	}{}
	m.statusMutex.Lock()
	defer m.statusMutex.Unlock()
	for _, door := range m.doorInfos {
		reply.DoorInfos = append(reply.DoorInfos, door)
	}
	for _, elevator := range m.elevatorInfos {
		reply.ElevatorInfos = append(reply.ElevatorInfos, elevator)
	}
	return reply

}

//-------------------设置所有的设备状态---------------
func (m *DeviceManager) SetAllDeviceStatus(doorInfos []model.DoorInfo, elevatorInfos []model.ElevatorStatus) {
	m.statusMutex.Lock()
	defer m.statusMutex.Unlock()
	for k := range m.doorInfos {
		delete(m.doorInfos, k)
	}

	for _, door := range doorInfos {
		if _, find := m.doorMap[door.DeviceCode]; !find {
			continue
		}
		door.IsEnable = m.doorMap[door.DeviceCode].IsEnable
		door.MapName = m.doorMap[door.DeviceCode].MapName
		m.doorInfos[door.DeviceCode] = door
	}
	for k := range m.elevatorInfos {
		delete(m.elevatorInfos, k)
	}

	for _, elevator := range elevatorInfos {
		if _, find := m.elevatorMap[elevator.DeviceCode]; !find {
			continue
		}
		elevator.IsEnable = m.elevatorMap[elevator.DeviceCode].IsEnable
		elevator.AgvId = m.elevatorMap[elevator.DeviceCode].AgvId
		//存储数据数组
		levelStatusList := []model.LevelStatus{}
		//找到的电梯便利所有层
		for _, levelInfo := range m.elevatorMap[elevator.DeviceCode].InfoList {
			var levelStatus model.LevelStatus
			//1.解析当前地图所在层
			levelStr := strings.Split(strings.Split(levelInfo.MapName, "@")[0], "-")[1]
			levelInt, err := strconv.Atoi(levelStr)
			if err != nil {
				levelStatus.CurrentLevel = -1
			} else {
				levelStatus.CurrentLevel = levelInt
			}
			//2.地图赋值r
			levelStatus.MapName = levelInfo.MapName
			levelStatus.CurrentSiteId = levelInfo.StationId
			//3.解析当前层的状态r
			//故障-所有层
			if elevator.Status == model.EFault {
				levelStatus.CurrentStatus = model.EFault
			} else {
				//当前层
				if elevator.Level == levelInt && elevator.Status == model.EOpened {
					levelStatus.CurrentStatus = model.EOpened
				} else {
					levelStatus.CurrentStatus = model.EClosed
				}
			}

			levelStatusList = append(levelStatusList, levelStatus)
		}
		elevator.LevelStatusList = levelStatusList
		m.elevatorMap[elevator.DeviceCode].Online = elevator.Online
		m.elevatorInfos[elevator.DeviceCode] = elevator
	}
}

//*********缓存相关***************
//loadAllElevatorToCache 加载电梯数据到缓存
func (m *DeviceManager) loadAllElevatorToCache(ctx context.Context) codes.Code {
	elevatorList, errGetAllelevators := m.GetAllElevatorFromDB(ctx)
	if errGetAllelevators != nil {
		log.ErrorfWithContext(ctx, "load robots to cache error,can not get robot accounts:%s", errGetAllelevators.UnWrapError())
		return errGetAllelevators
	}
	m.elevatorMutex.Lock()
	defer m.elevatorMutex.Unlock()
	for _, elevator := range elevatorList {
		m.addElevatorToCache(ctx, elevator)
	}
	return nil
}

//loadAllSwitchFactoryRelation 加载厂区切换点的缓存
// func (m *DeviceManager) loadAllSwitchFactoryRelationToCache(ctx context.Context) codes.Code {

// }

func (m *DeviceManager) MapUseInElevator(ctx context.Context, mapName string) bool {
	for _, elevator := range m.elevatorMap {
		for _, mapInfo := range elevator.InfoList {
			if mapInfo.MapName == mapName {
				return true
			}
		}
	}
	return false
}

//GetAllElevatorFromDB 从数据库获得所有电梯信息
func (m *DeviceManager) GetAllElevatorFromDB(ctx context.Context) ([]*model.Elevator, codes.Code) {
	dbRes, err := m.db.Collection(ctx, model.Elevator{}.TableName()).Find(ctx, bson.D{})
	if err != nil {
		log.ErrorfWithContext(ctx, "get all elevator from db error:%s", err.Error())
		return nil, codes.ErrDatabase.Wrap(err)
	}
	elevatorList := make([]*model.Elevator, 0)
	if errBind := dbRes.All(ctx, &elevatorList); errBind != nil {
		log.ErrorfWithContext(ctx, "bind all elevator from db error:%s", errBind.Error())
		return nil, codes.ErrDatabase.Wrap(errBind)
	}
	return elevatorList, nil
}

//GetAllElevatorFromCache 从缓存获得所有电梯信息
func (m *DeviceManager) GetAllElevatorFromCache() map[string]*model.Elevator {
	return m.elevatorMap
}

//addElevatorToCache 添加elevator到缓存，方便状态更新和获得
func (m *DeviceManager) addElevatorToCache(ctx context.Context, elevator *model.Elevator) {
	//log.Infof("add robot:%s", robotAccount.AgvId)
	m.elevatorMap[elevator.DeviceCode] = elevator
}
func (m *DeviceManager) GetElevatorInCache(ctx context.Context, deviceId string) *model.Elevator {
	//log.Infof("add robot:%s", robotAccount.AgvId)
	elevator, find := m.elevatorMap[deviceId]
	if find {
		return elevator
	}
	return nil
}
